﻿// (c) Norbert Huffschmid
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace SpinDotters.Core
{
    /// <summary>
    /// Segments bar, e.g. for usage in SpinningProgressBar controls 
    /// </summary>
    [TemplatePart(Name = "PART_RangeOpacityBrush", Type = typeof(LinearGradientBrush))]
    [TemplatePart(Name = "PART_SegmentsOpacityBrush", Type = typeof(LinearGradientBrush))]
    public class SegmentBar : Control
    {
        private const int SEGMENTS_DEFAULT = 50;

        private LinearGradientBrush rangeOpacityBrush;
        private LinearGradientBrush segmentsOpacityBrush;


        /// <summary>
        /// Identifies the Segments dependency property.
        /// </summary>
        public static readonly DependencyProperty SegmentsProperty =
            DependencyProperty.Register(
                "Segments",
                typeof(int),
                typeof(SegmentBar),
                new PropertyMetadata(SEGMENTS_DEFAULT, new PropertyChangedCallback(OnLayoutChanged))
            );

        /// <summary>
        /// Gets or sets the number of segments.
        /// </summary>
        [Category("SpinDotters")]
        public int Segments
        {
            get { return (int)GetValue(SegmentsProperty); }

            set
            {
                if (value < 1)
                    throw new ArgumentOutOfRangeException("Invalid value for number of segments");

                SetValue(SegmentsProperty, value);
            }
        }

        /// <summary>
        /// Identifies the StartSegment dependency property.
        /// </summary>
        public static readonly DependencyProperty StartSegmentProperty =
            DependencyProperty.Register(
                "StartSegment",
                typeof(int),
                typeof(SegmentBar),
                new PropertyMetadata(0, new PropertyChangedCallback(OnLayoutChanged))
            );

        /// <summary>
        /// Gets or sets the start segment.
        /// </summary>
        [Category("SpinDotters")]
        public int StartSegment
        {
            get { return (int)GetValue(StartSegmentProperty); }

            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("Invalid value for start segment");

                SetValue(StartSegmentProperty, value);
            }
        }

        /// <summary>
        /// Identifies the EndSegment dependency property.
        /// </summary>
        public static readonly DependencyProperty EndSegmentProperty =
            DependencyProperty.Register(
                "EndSegment",
                typeof(int),
                typeof(SegmentBar),
                new PropertyMetadata(SEGMENTS_DEFAULT, new PropertyChangedCallback(OnLayoutChanged))
            );

        /// <summary>
        /// Gets or sets the end segment.
        /// </summary>
        [Category("SpinDotters")]
        public int EndSegment
        {
            get { return (int)GetValue(EndSegmentProperty); }

            set
            {
                if (value > this.Segments)
                    throw new ArgumentOutOfRangeException("Invalid value for end segment");

                SetValue(EndSegmentProperty, value);
            }
        }

        /// <summary>
        /// Identifies the SegmentBrush dependency property.
        /// </summary>
        public static readonly DependencyProperty SegmentBrushProperty =
            DependencyProperty.Register(
                "SegmentBrush",
                typeof(Brush),
                typeof(SegmentBar),
                null
            );

        /// <summary>
        /// Gets or sets the segment brush.
        /// </summary>
        [Category("SpinDotters")]
        public Brush SegmentBrush
        {
            get { return (Brush)GetValue(SegmentBrushProperty); }
            set { SetValue(SegmentBrushProperty, value); }
        }

        /// <summary>
        /// Orientation dependency property.
        /// </summary>
        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register(
                "Orientation",
                typeof(Orientation),
                typeof(SegmentBar),
                new PropertyMetadata(new PropertyChangedCallback(OnLayoutChanged))
            );

        /// <summary>
        /// Gets or sets the spin orientation.
        /// </summary>
        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }


        /// <summary>
        /// Constructor
        /// </summary>
        public SegmentBar()
        {
            this.DefaultStyleKey = typeof(SegmentBar);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.rangeOpacityBrush = this.GetTemplateChild("PART_RangeOpacityBrush") as LinearGradientBrush;
            this.segmentsOpacityBrush = this.GetTemplateChild("PART_SegmentsOpacityBrush") as LinearGradientBrush;

            SetLayoutValues();
        }

        private void SetLayoutValues()
        {
            if ((this.segmentsOpacityBrush != null) && (this.rangeOpacityBrush != null))
            {
                if (this.Orientation == Orientation.Horizontal)
                {
                    this.MinWidth = 200;
                    this.MinHeight = 0;

                    // create vertical gaps between segments
                    this.segmentsOpacityBrush.StartPoint = new Point(1.0 - 1.0 / this.Segments, 0.5);
                    this.segmentsOpacityBrush.EndPoint = new Point(1, 0.5);

                    // create horizontal segment opacity
                    this.rangeOpacityBrush.StartPoint = new Point(0, 0.5);
                    this.rangeOpacityBrush.EndPoint = new Point(1, 0.5);
                }
                else
                {
                    this.MinWidth = 0;
                    this.MinHeight = 200;

                    // create horizontal gaps between segments
                    this.segmentsOpacityBrush.StartPoint = new Point(0.5, 1.0 - 1.0 / this.Segments);
                    this.segmentsOpacityBrush.EndPoint = new Point(0.5, 1);

                    // create vertical segment opacity
                    this.rangeOpacityBrush.StartPoint = new Point(0.5, 1);
                    this.rangeOpacityBrush.EndPoint = new Point(0.5, 0);
                }

                // create leading invisible region
                double startOffset = 1.0 * this.StartSegment / this.Segments;
                this.rangeOpacityBrush.GradientStops[0].Offset = startOffset;
                this.rangeOpacityBrush.GradientStops[1].Offset = startOffset;

                // create trailing invisible region
                double endOffset = (1.0 * this.EndSegment) / this.Segments;
                this.rangeOpacityBrush.GradientStops[2].Offset = endOffset;
                this.rangeOpacityBrush.GradientStops[3].Offset = endOffset;
            }
        }

        private static void OnLayoutChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((SegmentBar)o).SetLayoutValues();
        }
    }
}
